package com.bangcommunity.bbframe.sdm.aop.wrapper;

import com.bangcommunity.bbframe.common.utils.lang.CollectionUtils;
import com.bangcommunity.bbframe.sdm.aop.handler.AopHandler;
import com.bangcommunity.bbframe.sdm.aop.handler.ExceptionHandler;
import com.bangcommunity.bbframe.sdm.aop.handler.FinalHandler;
import com.bangcommunity.bbframe.sdm.aop.handler.PreHandler;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author tanghc
 * @date 17/1/18
 */
public class BaseAopWrapper implements AopWrapper {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());
    protected List<PreHandler> preHandlers = new ArrayList<>();
    protected List<ExceptionHandler> exceptionHandlers = new ArrayList<>();
    protected List<FinalHandler> finalHandlers = new ArrayList<>();
    protected AopWrapper nextWrapper = null;

    public BaseAopWrapper(AopWrapper nextWrapper) {
        this.nextWrapper = nextWrapper;
    }

    @Override
    public void addHandlers(AopHandler handler) {
        if (null != handler) {
            if (handler instanceof PreHandler) {
                preHandlers.add((PreHandler) handler);
            } else if (handler instanceof ExceptionHandler) {
                exceptionHandlers.add((ExceptionHandler) handler);
            } else if (handler instanceof FinalHandler) {
                finalHandlers.add((FinalHandler) handler);
            }
        }
    }

    @Override
    public Object wrap(ProceedingJoinPoint pjp) throws Exception {
        Object[] args = null;
        Object result = null;
        long starttime = System.currentTimeMillis();
        try {

            if (CollectionUtils.isNotEmpty(preHandlers)) {
                for (PreHandler handler : preHandlers) {
                    handler.setStartTime(starttime);
                    if (!handler.handler(pjp)) {
                        break;
                    }
                }
            }
            Object o = doNextWrapper(pjp);
            return wrapperResult(pjp, o);
        } catch (Throwable throwable) {
            Exception e = null;
            if (throwable instanceof Exception) {
                e = (Exception) throwable;
            } else {
                e = new Exception(throwable);
            }
            if (CollectionUtils.isNotEmpty(exceptionHandlers)) {
                for (ExceptionHandler handler : exceptionHandlers) {
                    handler.setTargetException(e);
                    if (!handler.handler(pjp)) {
                        break;
                    }
                }
            }
            return returnWhenException(pjp, e);
        } finally {
            long endtime = System.currentTimeMillis();
            if (CollectionUtils.isNotEmpty(finalHandlers)) {
                for (FinalHandler handler : finalHandlers) {
                    handler.setEndTime(endtime);
                    handler.setEndTime(endtime - starttime);
                    handler.setResult(result);
                    if (!handler.handler(pjp)) {
                        break;
                    }
                }
            }
        }
    }

    protected Object returnWhenException(ProceedingJoinPoint pjp, Exception e) throws Exception {
        throw e;
    }

    protected Object doNextWrapper(ProceedingJoinPoint pjp) throws Throwable {
        if (null == nextWrapper) {
            return doCoreWrapper(pjp);
        } else {
            return nextWrapper.wrap(pjp);
        }
    }

    protected Object doCoreWrapper(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs();
        Object result = pjp.proceed(args);
        return result;
    }

    protected Object wrapperResult(ProceedingJoinPoint pjp, Object result) {
        return result;
    }
}
